package com.livestocksolutions;

import java.util.Date;
import java.util.HashMap;

import com.livestocksolutions.util.UpdateHistory;
/*
 * Version History
 * 1.0 : 	Start:10 July 2012
 * 		 	End:  10 July 2012
 *  - Initial Version
 * 
 * 1.1 : Changed variable quantity from a float to a double
 * 
 * 1.2 : 06 September 2012 - Changed item from type UsableItem to long to store only the
 *                   reference to the UsableItem
 * 1.3 : 18 September 2012 - Added sedDate(Date) method to allow the setting of a date when it is not set.
 *                   added extra Constructor to accept a date.
 *  
 *  1.4 : 24 September 2012 - Added Update methods that will allow the comparing and updation of a similar Object.
 *                   Serialisation method no longer requires MedicalHistory implementing Serializable.
 *  
 *  2.0 : 07 January 2013 - Moved attributes from UsableItem class to UsableItemHistory class.
 */
/**
 * This class provides the basics for a UsableItemhistory
 * @author Ken DeVries
 * @.started  10 July 2012
 * @.modified 07 January 2013
 * @version 2.0
 */
public class UsableItemHistory{
	/**
	 * The ID of this UsableItemHistory.
	 */
	protected long id;
	/**
	 * Keep track of any changes of this UsableItemHistory.
	 */
	protected HashMap<String,UpdateHistory> update = new HashMap<String,UpdateHistory>();
	/**
	 * The reference to the item that has been used in this Item History.
	 */
	protected long item;
	/**
	 * To store the date of use for this Item History.
	 */
	protected long date;
	/**
	 * How much of this item that was used.
	 */
	protected double quantity;
	/**
	 * The measurement the {@link #quantity} was measured in.
	 */
	protected String measurement;
	/**
	 * The Origin of this item
	 */	
	protected long origin;
	/**
	 * The cost of the item.
	 */	
	protected double cost;
	
	/**
	 * Who issued this Item History
	 */
	protected String issuedBy;
	
	/**
	 * Construtor that accepts item / quantity and measurement.<br>
	 * Sets <b>issuedBy</b> to " "<br>
	 * @param item The Item that was used.
	 * @param quantity The quanity used.
	 * @param measurement The measurement units the quantity was measured in.
	 */
	public UsableItemHistory(UsableItem<?> item, Double quantity, String measurement){
		setItem(item);
		setQuantity(quantity, measurement);
		
		//TODO Produce proper issuedBy
		this.issuedBy = " ";
	}
	
	/**
	 * Construtor that accepts item / quantity and measurement.<br>
	 * Sets <b>issuedBy</b> to " "<br>
	 * @param item The Item that was used.
	 * @param quantity The quanity used.
	 * @param measurement The measurement units the quantity was measured in.
	 * @param date The date that this item was used.
	 */
	public UsableItemHistory(UsableItem<?> item, Double quantity, String measurement, Date date){
		setItem(item);
		setQuantity(quantity, measurement);
		setDate(date);
		//TODO Produce proper issuedBy
		this.issuedBy = " ";
	}
	
	/**
	 * Set the ID of this UsableItemHistory.
	 * @param id The new id of this UsableItemHistory.
	 */
	public void setID(long id){
		if(this.id==0){
			this.id=id;
		}
	}
	
	/**
	 * Get the id of this UsableHistory item.
	 * @return The id of this UsableItemHistory.
	 */
	public long getID(){
		return id;
	}
	
	/**
	 * Gets the item used in this object
	 * @return item The item used in this object
	 */
	public UsableItem<?> getItem(){
		return LivestockSolutions.getUsableItemByID(item);
	}
	/**
	 * Returns the date used in this object
	 * @return date
	 */
	public Date getDate(){
		return date==0?null:new Date(date);
	}
	
	/**
	 * Set the date if it has not been set to the given date
	 * @param date The new date to set this items used date.
	 * @return <tt>true</tt> if the date was set to the given date.
	 * <tt>false</tt> otherwise.
	 */
	public boolean setDate(Date date){
		if(date!=null&&this.date!=date.getTime()){
			update.put("DATE", new UpdateHistory(String.valueOf(this.date)));
			this.date=date.getTime();
			return true;
		}
		return false;
	}
	/**
	 * Returns the quantity used by this object
	 * @return quantity
	 */
	public double getQuantity(){
		return quantity;
	}
	/**
	 * Get the measurement unit of the quantity
	 * @return measurement unit of the quantity
	 */
	public String getMeasurement(){
		return measurement;
	}
	/**
	 * Returns the issuedBy used by this object
	 * @return issuedBy
	 */
	public String getIssuedBy(){
		return issuedBy;
	}
	/**
	 * Sets the value of item for this object
	 * @param item
	 */
	public void setItem(UsableItem<?> item){
		if(item!=null&&item.getID()!=this.item){
			update.put("ITEM", new UpdateHistory(String.valueOf(this.item)));
			this.item = item.getID();
		}
	}
	
	/**
	 * Get the cost of the item that was used.
	 * @return Cost of the amount of this item used.
	 */
	public double getCost() {
		return cost;
	}
	/**
	 * Sets the value of quantity for this 
	 * @param quantity The amount of the Item that had been consumed
	 * @param measurement The measurement of the quantity of the item
	 * @return <tt>true</tt> if the quantity is accepted, <tt>false</tt> otherwise.
	 */
	public boolean setQuantity(double quantity, String measurement){
		if(measurement!=null&&!measurement.equals("")){
			UsableItem<?> i = LivestockSolutions.getUsableItemByID(item);
			double calculatedQuantity = i.convertToSameUnits(quantity, measurement);
			
			if(calculatedQuantity>=0){
				this.quantity=quantity;
				this.measurement=measurement;
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Get the place of manufacture of the item
	 * @return place of manufacture of the item
	 */
	public Business getOrigin(){
		Owner o = LivestockSolutions.getOwnerByID(origin);
		if(!(o instanceof Business)&& o != null){
			// Change back type to Business
			LivestockSolutions.addOwner(o=o.changeType(Business.class));
		}
		return (Business)o;
	}
	
	/**
	 * Get the amount of this item that has currently been used.<br>
	 * Measured in the same units as the total quantity purchased.
	 * @return amount of this item that has be used
	 */
	public double getAmountUsed(){
		return quantity;
	}
	
	/**
	 * Set the place of manufacturer of this item.
	 * @param origin the place where this item it made from
	 */
	public void setOrigin(Business origin){
		if(origin!=null&&this.origin!=origin.getID()){
			this.origin=origin.getID();
			update.put("ORIGIN", new UpdateHistory(String.valueOf(this.origin)));
		}
	}
	
	/**
	 * Set the cost to purchase this item.
	 * @param cost purchase price of this item
	 */
	public void setCost(double cost){
		if(this.cost!=cost){
			this.cost=cost;
			update.put("COST", new UpdateHistory(String.valueOf(this.cost)));
		}
	}
	
	/**
	 * Updates this Object with the updated values from the different version.
	 * @param changes UsableItemHistory that represents this Object in a different version.
	 * @return <tt>true</tt>.
	 */
	public boolean update(UsableItemHistory changes){
		if(changes!=null && this!=changes){
			if(updateBefore("DATE",changes)){
				date=Long.parseLong(changes.update.get("DATE").getValue());
				update.put("DATE", new UpdateHistory(String.valueOf(date)));
			}
			
			if(updateBefore("ITEM",changes)){
				item=Long.parseLong(changes.update.get("ITEM").getValue());
				update.put("ITEM", new UpdateHistory(String.valueOf(item)));
			}
			
			if(updateBefore("MEASUREMENT",changes)){
				measurement=changes.update.get("MEASUREMENT").getValue();
				update.put("MEASUREMENT", new UpdateHistory(measurement));
			}
			
			if(updateBefore("QUANTITY",changes)){
				quantity=Double.parseDouble(changes.update.get("QUANTITY").getValue());
				update.put("QUANTITY", new UpdateHistory(String.valueOf(quantity)));
			}
			
			if(updateBefore("ORIGIN",changes)){
				this.origin=Long.valueOf(changes.update.get("ORIGIN").getValue());
				update.put("ORIGIN", new UpdateHistory(String.valueOf(this.origin)));
			}
			
			if(updateBefore("COST",changes)){
				this.cost=Double.valueOf(changes.update.get("COST").getValue());
				update.put("COST", new UpdateHistory(String.valueOf(this.cost)));
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Checks to see if the modification date of the item at this position is before the changed value
	 * @param key The String key at which to compare the modification dates of the modified objects.
	 * @param changes The UsableItemHistory that represents this UsableItemHistory on another system.
	 * @return <tt>true</tt> if the old value was modified before the changed value. <tt>false</tt> otherwise.
	 */
	protected boolean updateBefore(String key,UsableItemHistory changes){
		if(this.update.get(key)==null && changes.update.get(key)!=null){
			return true;
		}else if(changes.update.get(key)==null){
			return false;
		}
		return !this.update.get(key).getDate().after(changes.update.get(key).getDate());
	}
}
